package com.rent.collectrent.service.impl;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.rent.collectrent.dto.WriteOffRequest;
import com.rent.collectrent.entity.BankReceipt;
import com.rent.collectrent.entity.RentPlan;
import com.rent.collectrent.entity.WriteOffDetail;
import com.rent.collectrent.entity.WriteOffSummary;
import com.rent.collectrent.mapper.BankReceiptMapper;
import com.rent.collectrent.mapper.RentPlanMapper;
import com.rent.collectrent.mapper.WriteOffDetailMapper;
import com.rent.collectrent.mapper.WriteOffSummaryMapper;
import com.rent.collectrent.service.ProcessBankReceiptsService;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author news
 * *@date 2025/10/16
 */
@Service
@Slf4j
public class ProcessBankReceiptsServiceImpl implements ProcessBankReceiptsService {


    @Autowired
    private BankReceiptMapper bankReceiptMapper;

    @Autowired
    private RentPlanMapper rentPlanMapper;

    @Autowired
    private WriteOffDetailMapper writeOffDetailMapper;

    @Autowired
    private WriteOffSummaryMapper writeOffSummaryMapper;

    private final ThreadPoolExecutor writeOffExecutor = new ThreadPoolExecutor(
            10, 20, 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(200),
            new ThreadFactoryBuilder().setNameFormat("write-off-pool-%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );


    /**
     * 执行核销
     * * 这里可以使用中间件实现异步
     * @param request
     * @param batchNo
     * @param summary
     * @param startTime
     */
    @Async("taskExecutor")
    public void processedBankReceiptsAsync(WriteOffRequest request, String batchNo, WriteOffSummary summary, long startTime) {
        // 分页处理银行收款记录
        ProcessSingleResult receipt = processBankReceiptsInBatches(batchNo, request);
        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            log.error("模拟运行等待");
        }
        // 更新汇总信息
        writeOffSummaryMapper.updateSummaryByBatchNo(
                summary.getBatchNo(),
                receipt.getProcessedCount(),
                receipt.getPrincipalToWriteOff(),
                receipt.getInterestToWriteOff(),
                System.currentTimeMillis() - startTime,
                "COMPLETED"
        );
        log.info("批量核销完成，批次号: {}, 处理记录数: {}", batchNo, receipt.getProcessedCount());
    }

    /**
     * 分批分页处理银行收款记录
     * @param batchNo
     * @param request
     * @return
     */
    private ProcessSingleResult processBankReceiptsInBatches(String batchNo, WriteOffRequest request) {
        ProcessSingleResult singleResult = ProcessSingleResult.builder().processedCount(0).interestToWriteOff(BigDecimal.ZERO).principalToWriteOff(BigDecimal.ZERO).build();
        int page = 0;
        int pageSize = request.getBatchSize();
        while (true) {
            // 分页查询未使用的银行收款记录
            List<BankReceipt> receipts = bankReceiptMapper.selectUnusedReceipts(page * pageSize, pageSize);

            if (receipts.isEmpty()) {
                break;
            }
            // 使用多线程处理当前页的记录
            List<CompletableFuture<ProcessSingleResult>> futures = receipts.stream()
                    .map(receipt -> CompletableFuture.supplyAsync(() -> processSingleReceipt(receipt, batchNo), writeOffExecutor))
                    .collect(Collectors.toList());

            // 等待所有任务完成
            List<ProcessSingleResult> results = futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
            if (!results.isEmpty()) {
                results.stream().filter(r -> r.getProcessedCount() > 0).forEach(r -> {
                    log.info("任务：{}", r.toString());
                    singleResult.setProcessedCount(singleResult.getProcessedCount()+r.getProcessedCount());
                    singleResult.setInterestToWriteOff(singleResult.getInterestToWriteOff().add(r.getInterestToWriteOff()));
                    singleResult.setPrincipalToWriteOff(singleResult.getPrincipalToWriteOff().add(r.getPrincipalToWriteOff()));
                });
            }
            page++;
            log.info("批次号: {}, 第{}页处理完成，本页处理{}条记录", batchNo, page, receipts.size());
        }
        return singleResult;
    }

    private ProcessSingleResult processSingleReceipt(BankReceipt receipt, String batchNo) {
        int processedCount = 0;
        // 总核销利息
        BigDecimal totalInterestToWriteOff = BigDecimal.ZERO;
        // 总核销本金
        BigDecimal totalPrincipalToWriteOff = BigDecimal.ZERO;
        // 核销利息
        BigDecimal interestToWriteOff = BigDecimal.ZERO;
        // 核销本金
        BigDecimal principalToWriteOff = BigDecimal.ZERO;
        // 可使用金额
        BigDecimal remainingAmount = receipt.getPaymentAmount().subtract(receipt.getUsedAmount());
        if (remainingAmount.compareTo(BigDecimal.ZERO) <= 0) {
            return ProcessSingleResult.builder()
                    .processedCount(processedCount).interestToWriteOff(interestToWriteOff).principalToWriteOff(principalToWriteOff).build();
        }
        try {
            // 根据付款人名称查找对应的租金计划
            List<RentPlan> rentPlans = rentPlanMapper.selectByTenantName(
                    receipt.getPayerName(),
                    RentPlan.WriteOffStatus.UNWRITTEN.name(),
                    RentPlan.WriteOffStatus.PARTIAL_WRITTEN.name());

            List<WriteOffDetail> details = new ArrayList<>();

            for (RentPlan plan : rentPlans) {

                // 计算可核销的利息和本金
                BigDecimal availableInterest = plan.getInterestAmount().subtract(plan.getReceivedInterest());
                BigDecimal availablePrincipal = plan.getPrincipalAmount().subtract(plan.getReceivedPrincipal());

                // 核销利息
                interestToWriteOff = remainingAmount.min(availableInterest);
                // 核销本金
                principalToWriteOff =BigDecimal.ZERO;
                if (remainingAmount.compareTo(availableInterest) > 0) {
                    // 如果可用金额大于可核销利息
                    interestToWriteOff  = availableInterest;
                    principalToWriteOff = remainingAmount.subtract(interestToWriteOff).min(availablePrincipal);
                }

                // 核销总金额
                BigDecimal totalWriteOff = interestToWriteOff.add(principalToWriteOff);
                if (totalWriteOff.compareTo(BigDecimal.ZERO) > 0) {
                    // 使用乐观锁更新租金计划
                    int updated = rentPlanMapper.updateWithLock(
                            plan.getId(),
                            plan.getVersion(),
                            interestToWriteOff,
                            principalToWriteOff
                    );
                    if (updated > 0) {
                        remainingAmount = remainingAmount.subtract(interestToWriteOff).subtract(principalToWriteOff);
                        processedCount++;

                        // 创建核销明细记录
                        WriteOffDetail detail = new WriteOffDetail();
                        detail.setReceiptId(receipt.getId());
                        detail.setRentPlanId(plan.getId());
                        detail.setWriteOffAmount(totalWriteOff);
                        detail.setWriteOffInterest(interestToWriteOff);
                        detail.setWriteOffPrincipal(principalToWriteOff);
                        detail.setWriteOffDate(new Date());
                        detail.setBatchNo(batchNo);

                        details.add(detail);
                        totalInterestToWriteOff = totalInterestToWriteOff.add(interestToWriteOff);
                        totalPrincipalToWriteOff = totalPrincipalToWriteOff.add(principalToWriteOff);
                    }
                }
            }
            // 批量插入核销明细
            if (!details.isEmpty()) {
                writeOffDetailMapper.batchInsert(details);
            }
            // 更新银行收款记录的使用状态
            updateBankReceiptUsage(receipt, remainingAmount);

        } catch (Exception e) {
            log.error("处理银行收款记录失败，ID: {}", receipt.getId(), e);
        }
        ProcessSingleResult singleResult = ProcessSingleResult.builder()
                .processedCount(processedCount).interestToWriteOff(totalInterestToWriteOff).principalToWriteOff(totalPrincipalToWriteOff).build();
        System.out.println("singleResult = " + singleResult);
        return singleResult;
    }

    private void updateBankReceiptUsage(BankReceipt receipt, BigDecimal remainingAmount) {
        BigDecimal usedAmount = receipt.getPaymentAmount().subtract(remainingAmount);

        if (usedAmount.compareTo(BigDecimal.ZERO) == 0) {
            bankReceiptMapper.updateUsageStatus(receipt.getId(), usedAmount, BankReceipt.UsageStatus.UNUSED);
        } else if (remainingAmount.compareTo(BigDecimal.ZERO) > 0) {
            bankReceiptMapper.updateUsageStatus(receipt.getId(), usedAmount, BankReceipt.UsageStatus.PARTIAL_USED);
        } else {
            bankReceiptMapper.updateUsageStatus(receipt.getId(), usedAmount, BankReceipt.UsageStatus.USED);
        }
    }





    @Data
    @Builder
    public static class ProcessSingleResult {
        private int processedCount;
        private BigDecimal interestToWriteOff;
        private BigDecimal principalToWriteOff;

        @Override
        public String toString() {
            return "ProcessSingleResult{" + "processedCount=" + processedCount + ", interestToWriteOff=" + interestToWriteOff + ", principalToWriteOff=" + principalToWriteOff + '}';
        }
    }

}
